home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Carousel
/
CAROUSEL.cdr
/
mactosh
/
utilprn
/
hpdeskje.sit
/
HPDJet ƒ
/
PDEF1.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-04-02
|
20KB
|
649 lines
/* 02.04.1989 amn (latest edit) */
/* PDEF1.c - printer driver for Macintosh and HP DeskJet, spooling. */
/* Compiles into 'PDEF' resource, id 1, name ''. */
/* We cannot use any global variables in this code. However, the globals of the */
/* low-level driver (XPrint) are available thru the handle 'dCtlStorage' in the */
/* driver's device control entry. */
/* This resource is placed into the printer resource file */
/* 'HP DeskJet', type 'PRER', creator 's89^' as */
/* 'PDEF' 1 '' by 'PRER_Builder' utility program. This utility adds a small jump */
/* table in front of the code produced by LightspeedC. */
/* These procedures handle spool file recording... */
/* Authors: Ari Mujunen (amn@hutcs.hut.fi) and Olli Arnberg (oar@hutcs.hut.fi). */
/* Copyright Ari Mujunen, Olli Arnberg 1989. */
/* You may redistribute the driver (=printer resource file, source files, */
/* documentation file(s), and the file 'Copyright and Source Offer') */
/* only _non-commercially_ and _in its entirety_. */
/* See the file 'Copyright and Source Offer' and/or documentation for details. */
/* Acknowledgements: Special thanks to Mr. Earle R. Horton for his 'Daisy' */
/* daisywheel printer driver and its source code published in 'MacTutor', Nov-Dec 1987. */
/* This driver served as a basis and inspiration for our work. It also */
/* proofed that a Macintosh printer driver can be done despite the lack of */
/* documentation from Apple. */
/* Change history: */
/* Version When Who Why */
/* 2.0 09.12.1988 amn Original rewrite. */
/* 11.12.1988 amn Cleaning up. */
/* 15.12.1988 amn Differentiate between our idle proc */
/* that of application. */
/* 25.02.1989 amn Accidentally locked PDEF 0 instead of 1. */
/* 10.03.1989 amn Saving the length of each string drawn */
/* as a picture comment. */
/* 25.03.1989 amn Removing global file refNums. */
/* 28.03.1989 amn Settable printer origin. */
/* 30.03.1989 amn,oar Option-OK causes spool file to be named and */
/* saved for subsequent batch printing. */
/* 31.03.1989 amn More permanent spool file. */
/* 2.1 02.04.1989 amn,oar Released version. */
#include "common_mac_includes.h"
/* Mac OS includes specific to this module: */
#include <ToolboxUtil.h>
#include <StdFilePkg.h>
#include "prglobals.h"
#include "procedures_for_PDEF1.c"
#include "procedures_for_PDEF5.c" /* idle proc is here */
/* Function prototypes. */
/* Procedure dispatcher. */
void main(int);
/* Initializes a specialized grafPort for printing. Opens spool file. */
pascal TPPrPort myPrOpenDoc(THPrint, TPPrPort, Ptr);
/* Disposes the printing grafPort. Updates spool file header on disk. Closes spool file. */
pascal void myPrCloseDoc(TPPrPort);
/* Reinitializes the printing grafPort. */
pascal void myPrOpenPage(TPPrPort, TPRect);
/* Updates spool file header in memory. */
pascal void myPrClosePage(TPPrPort);
/* Replaces the standard QuickDraw low-level procedure for drawing text. */
pascal void myStdText(int, Ptr, Point, Point);
/* Used to call standard QD text-drawing procedure. We use prototypes... */
pascal void CallPascal(int, Ptr, Point, Point, Ptr);
pascal void putPICTData(Ptr, int);
void copystr(StringPtr, StringPtr);
/* Function definitions. */
void
main(routineSelector)
int routineSelector;
{
/* The jump table code inserted before our code resource by 'PRER_Builder' */
/* utility program pushes a word onto stack indicating which routine is called. */
/* We pop it off the stack and select an appropriate routine. */
switch(routineSelector) {
case 0:
asm {
unlk a6 ;; LSC generates an 'link' instruction to access parameters
move.l (a7)+, d0 ;; pop return address to our jump table code (discarded)
move.w (a7)+, d0 ;; pop argument 'routineSelector'
jmp myPrOpenDoc
}
case 1:
asm {
unlk a6
move.l (a7)+, d0
move.w (a7)+, d0
jmp myPrCloseDoc
}
case 2:
asm {
unlk a6
move.l (a7)+, d0
move.w (a7)+, d0
jmp myPrOpenPage
}
case 3:
asm {
unlk a6
move.l (a7)+, d0
move.w (a7)+, d0
jmp myPrClosePage
}
} /* switch */
/* We should not arrive here; Printing Manager has called a non-existent routine. */
SysError(5); /* check bounds trap ??? */
} /* main */
/*
This function is supposed to return a pointer to a specialized GrafPort (a TPrPort) customized
for printing. Due to the paucity of documentation on how to go about this, I do not know whether
I am going about this in exactly the right way, but I sure hope so. I set portBits.bounds for
the port to the empty Rect (0,0,0,0).
Other tasks: save a copy of the user print record in the printer resource file
to remember user's last choices in job dialog (resolution etc.).
*/
#define DEBUG_OPEN_PRINT_RECORD
pascal TPPrPort
myPrOpenDoc(hPrint, pPrPort, pIOBuf)
THPrint hPrint;
TPPrPort pPrPort;
Ptr pIOBuf;
{
TPPrPort thisPort;
ptXprintGlobals xPrintGlobals;
Boolean spoolFileExistedBeforeThisOpenDoc;
OSErr retCode;
{ /* Lock this code resource in memory (Printing Manager should do this, but...). */
Handle us;
us = (Handle)GetResource('PDEF', 1);
if (us == nil) {
PrintErr = ResError();
return(nil);
}
HLock(us);
}
if (pPrPort == nil) { /* printing GrafPort NOT preallocated */
if ((thisPort = (TPPrPort)NewPtr((long)sizeof(TPrPort))) == nil) {
PrintErr = iMemFullErr;
return(nil);
}
thisPort->fOurPtr = TRUE;
}
else { /* printing GrafPort already preallocated */
/* We cannot check the size of the port given to us, since it can be */
/* in application globals, not necessarily a block reserved with NewPtr. */
thisPort = pPrPort;
thisPort->fOurPtr = FALSE;
}
xPrintGlobals = GET_XPRINT_GLOBALS;
xPrintGlobals->hSpoolFileHeader = nil;
/* Initialize GrafPort */
OpenPort(&thisPort->gPort); /* could this cause a Memory Manager error ??? */
setRectsAndRgns(thisPort, hPrint);
/* Determine the name of the spool file. */
if (!xPrintGlobals->spoolFileAlreadyNamed) {
StringHandle defaultSpoolFileNameHandle;
StringPtr defaultSpoolFileNamePtr;
Str255 defaultSpoolFileName;
/* Page range etc. are available in global print record. */
xPrintGlobals->printRecord = **hPrint;
/* Default name from either print record or from resource. */
if (xPrintGlobals->printRecord.prJob.pFileName != nil) {
defaultSpoolFileNamePtr = xPrintGlobals->printRecord.prJob.pFileName;
/* We assume volume number is ok. */
}
else {
defaultSpoolFileNameHandle = GetString(RESID_OWNED_BY_PDEF + 1);
if (defaultSpoolFileNameHandle == nil)
copystr((StringPtr)"\pPrint File", defaultSpoolFileName);
else {
LoadResource(defaultSpoolFileNameHandle);
copystr(*defaultSpoolFileNameHandle, defaultSpoolFileName);
HPurge(defaultSpoolFileNameHandle);
}
defaultSpoolFileNamePtr = defaultSpoolFileName;
xPrintGlobals->printRecord.prJob.iFileVol = 0; /* default volume ??? */
}
if (xPrintGlobals->spoolFileIsNamedAndPermanent) {
Point where;
SFReply reply;
StringHandle hPrompt;
int saveVRefNum;
Str255 dummySaveVolName;
/* Set default volume for StdFile dialog starting point. */
if ((retCode = GetVol(&dummySaveVolName, &saveVRefNum)) != noErr)
goto cleanUp;
if ((retCode = SetVol(nil, xPrintGlobals->printRecord.prJob.iFileVol)) != noErr)
goto cleanUp;
where.v = where.h = 100;
/* Get prompt; (RESID+2 is used by SuperSpool...) */
if ((hPrompt = GetString(RESID_OWNED_BY_PDEF + 3)) != nil) {
HLock(hPrompt);
SFPutFile(
where,
*hPrompt,
defaultSpoolFileNamePtr, /* original name */
nil,
&reply
);
HUnlock(hPrompt);
HPurge(hPrompt);
}
else {
SFPutFile(
where,
(StringPtr)"\pSave spool file as:",
defaultSpoolFileName, /* original name */
nil,
&reply
);
}
if ((retCode = SetVol(nil, saveVRefNum)) != noErr)
goto cleanUp; /* default volume is probably messed up... */
if (reply.good) {
copystr(reply.fName, defaultSpoolFileName);
defaultSpoolFileNamePtr = defaultSpoolFileName;
xPrintGlobals->printRecord.prJob.iFileVol = reply.vRefNum;
xPrintGlobals->spoolFileAlreadyNamed = TRUE;
}
else { /* User clicked Cancel, resume normal printing. */
xPrintGlobals->spoolFileIsNamedAndPermanent = FALSE;
xPrintGlobals->spoolFileAlreadyNamed = FALSE;
}
} /* if permanent spool file */
/* Now we copy the spool file name from either application memroy */
/* or our temp variable to the global spoolFileName; global */
/* print record reJob.pFileName field points to this variable. */
{
copystr(defaultSpoolFileNamePtr, xPrintGlobals->spoolFileName);
xPrintGlobals->printRecord.prJob.pFileName = xPrintGlobals->spoolFileName;
/* xPrintGlobals->printRecord.prJob.iFileVol is already ok */
xPrintGlobals->printRecord.prJob.bFileVers = 0;
}
} /* end if has to determine spool file name */
/* Open spool file, set its file type ('PFIL''^89s'). */
/* '^89s' is the signature of the batch printing utility, BatchPrint. */
retCode = Create(
xPrintGlobals->printRecord.prJob.pFileName,
xPrintGlobals->printRecord.prJob.iFileVol,
'^89s',
'PFIL'
);
spoolFileExistedBeforeThisOpenDoc = (retCode == dupFNErr);
if (!((retCode == noErr) || (retCode == dupFNErr)))
goto cleanUp;
retCode = FSOpen(
xPrintGlobals->printRecord.prJob.pFileName,
xPrintGlobals->printRecord.prJob.iFileVol,
&(xPrintGlobals->spoolFileRefNum)
);
if (retCode != noErr)
goto deleteAndCleanUp;
/* Allocate memory buffer for page directory. */
if ((xPrintGlobals->hSpoolFileHeader = (THPfHeader)NewHandle((long)sizeof(TPfHeader))) == nil) {
retCode = iMemFullErr;
goto closeAndDeleteAndCleanUp;
}
/* Add pages to existing spool file _or_ initialize new spool file. */
if (spoolFileExistedBeforeThisOpenDoc) {
long count;
count = sizeof(TPfHeader);
HLock(xPrintGlobals->hSpoolFileHeader);
retCode = FSRead(xPrintGlobals->spoolFileRefNum, &count, *(xPrintGlobals->hSpoolFileHeader));
HUnlock(xPrintGlobals->hSpoolFileHeader);
if (retCode != noErr)
goto disposAndCloseAndDeleteAndCleanUp;
/* Position file to logical end to receive additional pages. */
if ((retCode = SetFPos(xPrintGlobals->spoolFileRefNum, fsFromLEOF, 0L)) != noErr)
goto disposAndCloseAndDeleteAndCleanUp;
}
else { /* Prepend print spool file header for PFIL file format: */
long count;
int i;
/* Initialize header to current print record and all zeroes. */
for (i=0;i<sizeof(TPfHeader);i++)
((unsigned char *)(*xPrintGlobals->hSpoolFileHeader))[i] = (unsigned char)0;
(*xPrintGlobals->hSpoolFileHeader)->print = **hPrint;
(*xPrintGlobals->hSpoolFileHeader)->print.prJob.pIdleProc = nil;
(*xPrintGlobals->hSpoolFileHeader)->print.prJob.pFileName = nil;
(*xPrintGlobals->hSpoolFileHeader)->print.prJob.iFileVol = 0;
count = sizeof(TPfHeader);
HLock(xPrintGlobals->hSpoolFileHeader);
retCode = FSWrite(xPrintGlobals->spoolFileRefNum, &count, *(xPrintGlobals->hSpoolFileHeader));
HUnlock(xPrintGlobals->hSpoolFileHeader);
if (retCode != noErr)
goto disposAndCloseAndDeleteAndCleanUp;
#define NUMBER_OF_THIS_PDEF 1
#include "save_last_used.c"
#undef NUMBER_OF_THIS_PDEF
/* If application has not requested an own idle procedure, we set our default one. */
if (xPrintGlobals->printRecord.prJob.pIdleProc == nil) {
xPrintGlobals->printRecord.prJob.pIdleProc = (ProcPtr)checkForCommandPeriod;
xPrintGlobals->applicationOwnsIdleProc = FALSE;
}
else
xPrintGlobals->applicationOwnsIdleProc = TRUE;
/* For example WriteNow seems to rely on 'pIdleProc' being non-nil. */
/* That is why we must set it -- although we don't call idle proc */
/* ourselves, application might be that friendly and do it for us. */
} /* else spool file is new */
/* High-level printing code always counts pages from 1 (IM II-156). */
xPrintGlobals->pageCounter = 1;
#ifdef DEBUG_OPEN_PRINT_RECORD
/* Print out the contents of the print record to find out what applications store into it. */
#endif DEBUG_OPEN_PRINT_RECORD
retCode = noErr;
cleanUp:
PrintErr = retCode;
return(thisPort);
/* An application conforming to IM II-155 will call 'PrCloseDoc' regardless the value of */
/* 'PrError'. It is 'PrCloseDoc's duty to free 'thisPort'. */
disposAndCloseAndDeleteAndCleanUp:
DisposHandle(xPrintGlobals->hSpoolFileHeader);
xPrintGlobals->hSpoolFileHeader = nil;
closeAndDeleteAndCleanUp:
(void)FSClose(xPrintGlobals->spoolFileRefNum);
deleteAndCleanUp:
(void)FSDelete(
xPrintGlobals->printRecord.prJob.pFileName,
xPrintGlobals->printRecord.prJob.iFileVol
);
goto cleanUp;
} /* myOpenDoc */
pascal void
myPrCloseDoc(pPrPort)
TPPrPort pPrPort;
{
ptXprintGlobals xPrintGlobals;
OSErr retCode;
if (pPrPort == nil) /* if opening did not succeed, we do nothing */
return;
xPrintGlobals = GET_XPRINT_GLOBALS;
if (PrintErr != noErr) { /* something has gone wrong, we must clean up our port */
retCode = PrintErr;
goto disposAndCloseAndDeleteAndCleanUp;
}
/* Now we are at the end of last page spooled, mark logical end of file. */
{
long filePosition;
if ((retCode = GetFPos(xPrintGlobals->spoolFileRefNum, &filePosition)) != noErr)
goto disposAndCloseAndDeleteAndCleanUp;
if ((retCode = SetEOF(xPrintGlobals->spoolFileRefNum, filePosition)) != noErr)
goto disposAndCloseAndDeleteAndCleanUp;
}
/* Update print spool file header (page directory) for PFIL file format: */
{
long count;
retCode = SetFPos(xPrintGlobals->spoolFileRefNum, fsFromStart, 0L);
if (retCode != noErr)
goto disposAndCloseAndDeleteAndCleanUp;
count = sizeof(TPfHeader);
HLock(xPrintGlobals->hSpoolFileHeader);
retCode = FSWrite(xPrintGlobals->spoolFileRefNum, &count, *xPrintGlobals->hSpoolFileHeader);
HUnlock(xPrintGlobals->hSpoolFileHeader);
if (retCode != noErr)
goto disposAndCloseAndDeleteAndCleanUp;
DisposHandle(xPrintGlobals->hSpoolFileHeader);
}
retCode = FSClose(xPrintGlobals->spoolFileRefNum);
if (retCode != noErr)
goto deleteAndCleanUp;
retCode = noErr;
cleanUp:
PrintErr = retCode;
ClosePort(pPrPort);
if (pPrPort->fOurPtr) /* if we allocated the port, we have to deallocate it too */
DisposPtr(pPrPort);
return;
disposAndCloseAndDeleteAndCleanUp:
if (xPrintGlobals->hSpoolFileHeader == nil)
goto cleanUp;
DisposHandle(xPrintGlobals->hSpoolFileHeader);
xPrintGlobals->hSpoolFileHeader = nil;
closeAndDeleteAndCleanUp:
(void)FSClose(xPrintGlobals->spoolFileRefNum);
deleteAndCleanUp:
(void)FSDelete(
xPrintGlobals->printRecord.prJob.pFileName,
xPrintGlobals->printRecord.prJob.iFileVol
);
goto cleanUp;
} /* myCloseDoc */
pascal void
myPrOpenPage(pPrPort, pPageFrame)
TPPrPort pPrPort;
TPRect pPageFrame;
{
ptXprintGlobals xPrintGlobals;
OSErr retCode;
if (pPrPort == nil)
/* There is not much we can do; */
/* the application will 'print' into a very odd GrafPort. */
return;
/* If (PrintErr != noErr), we assume we have a port to satisfy the application, */
/* but something has gone awry with the spool file. */
xPrintGlobals = GET_XPRINT_GLOBALS;
/* Port is returned to initial state (as initialized in OpenDoc). */
InitPort(pPrPort);
{
TPPrint pPrint;
pPrint = &xPrintGlobals->printRecord;
setRectsAndRgns(pPrPort, &pPrint);
}
xPrintGlobals->hPagePicture = nil;
/* For pages not to be printed, return a GrafPort which does not record a QD-picture. */
if ((xPrintGlobals->pageCounter < xPrintGlobals->printRecord.prJob.iFstPage)
|| (xPrintGlobals->pageCounter > xPrintGlobals->printRecord.prJob.iLstPage)
|| (PrintErr != noErr)) {
return;
}
/* Open QD-picture using either page rectangle */
/* or application-specified rectangle as picture frame. */
{
Rect pictureFrame;
if (pPageFrame != nil)
pictureFrame = *pPageFrame; /* application wants to scale */
else
pictureFrame = xPrintGlobals->printRecord.prInfo.rPage;
xPrintGlobals->hPagePicture = OpenPicture(&pictureFrame);
if (xPrintGlobals->hPagePicture == nil) {
PrintErr = iMemFullErr;
return;
}
}
/* Update page directory in memory (page picture starting offset). */
{
long position;
int i;
retCode = GetFPos(xPrintGlobals->spoolFileRefNum, &position);
if (retCode != noErr) {
PrintErr = retCode;
return;
}
/* Store start offset in spool file according to the header page number. */
i = (++((*xPrintGlobals->hSpoolFileHeader)->pfPgDir.iPages));
(*xPrintGlobals->hSpoolFileHeader)->pfPgDir.lPgPos[i]
= position;
}
/* Write the beginning of page picture data to spool file. */
{
long count;
count = (long)sizeof(Picture);
HLock(xPrintGlobals->hPagePicture);
retCode = FSWrite(xPrintGlobals->spoolFileRefNum, &count, *xPrintGlobals->hPagePicture);
HUnlock(xPrintGlobals->hPagePicture);
if (retCode != noErr) {
PrintErr = retCode;
return;
}
}
/* Set our own procedure for storing picture data directly to spool file. */
SetStdProcs(&pPrPort->gProcs);
pPrPort->gPort.grafProcs = &pPrPort->gProcs;
pPrPort->gProcs.textProc = (QDPtr)myStdText;
pPrPort->gProcs.putPicProc = (QDPtr)putPICTData;
ClipRect(&pPrPort->gPort.portRect); /* ImageWriter records this, so do we. */
/* Finders 4.1 & 6.0 seem to rely on printing GrafPort's font being application font. */
/* Turbo Pascal 1.1 seems not to like this, though. */
TextFont(applFont);
} /* myPrOpenPage */
pascal void
myPrClosePage(pPrPort)
TPPrPort pPrPort;
{
ptXprintGlobals xPrintGlobals;
if (pPrPort == nil) /* Guard against stupid applications */
return;
xPrintGlobals = GET_XPRINT_GLOBALS;
/* If we were recording QD-picture to disk, we close it. */
if (xPrintGlobals->hPagePicture != nil) {
ClosePicture(); /* close picture to get the end_of_picture -mark to disk */
pPrPort->gPort.grafProcs = nil; /* no more special drawing procs */
KillPicture(xPrintGlobals->hPagePicture);
xPrintGlobals->hPagePicture = nil;
}
/* If the capacity of spool file header is exceeded, cancel printing. */
if (((*xPrintGlobals->hSpoolFileHeader)->pfPgDir.iPages) >= iPfMaxPgs)
PrintErr = iPrAbort;
xPrintGlobals->pageCounter++;
} /* myClosePage */
pascal void
myStdText(byteCount, textBuffer, numer, denom)
int byteCount;
Ptr textBuffer;
Point numer, denom;
{
htApplicationComment lengthComment;
QDProcs stdProcs;
if ((lengthComment = (htApplicationComment)NewHandle(sizeof(tApplicationComment))) != nil) {
(*lengthComment)->applicationSignature = 's89^';
(*lengthComment)->localKind = 0;
(*lengthComment)->originalLengthOfNextTextInPixels =
TextWidth(textBuffer, 0, byteCount);
/* Hmm, what about 'numer' and 'denom' ? */
/* Seems to work ok though, even when QD has to scale */
/* the font when recording PICT. */
PicComment(100, sizeof(tApplicationComment), lengthComment);
DisposHandle(lengthComment);
}
/* Finally, draw text using standard procedure. */
SetStdProcs(&stdProcs);
CallPascal(byteCount, textBuffer, numer, denom, stdProcs.textProc);
} /* myStdText */
pascal void
putPICTData(dataPtr, byteCount)
Ptr dataPtr;
int byteCount;
{
long count;
ptXprintGlobals xPrintGlobals;
OSErr retCode;
if (PrintErr == noErr) {
count = byteCount;
xPrintGlobals = GET_XPRINT_GLOBALS;
retCode = FSWrite(xPrintGlobals->spoolFileRefNum, &count, dataPtr);
if (retCode != noErr)
PrintErr = retCode;
}
} /* putPICTData */
void
copystr(src, dst)
StringPtr src, dst;
/* This is for copying PASCAL strings (simple) */
{
asm {
clr.l d0
move.l src,a0
move.l dst,a1
move.b (a0),d0
loop:
move.b (a0)+,(a1)+
dbra d0,@loop
}
}